package core.agent;

import java.io.IOException;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Date;
import java.util.Hashtable;
import java.util.List;


import core.api.DFservice;
import core.api.DbData;
import core.api.DbManager;
import core.api.DbMonitoring;
import core.api.FilterEvent;
import core.api.MonitoringData;
import core.datamodel.Constants;
import core.datamodel.Element;
import core.datamodel.ResourceCounter;
import core.datamodel.Event;
import core.datamodel.Accountit;
import core.datamodel.Resource;
import core.tool.LoadEvents;
import jade.core.AID;
import jade.core.Agent;
import jade.core.behaviours.CyclicBehaviour;
import jade.core.behaviours.TickerBehaviour;
import jade.domain.DFService;
import jade.domain.FIPAException;
import jade.domain.FIPAAgentManagement.DFAgentDescription;
import jade.domain.FIPAAgentManagement.ServiceDescription;
import jade.lang.acl.ACLMessage;
import jade.util.Logger;

public class AgentManager extends Agent {

	private Logger log = Logger.getJADELogger(this.getClass().getName());


	private DbManager db;
	private Connection conDb;
	private DbData dbData;



	private DFservice DFsrv = new DFservice();;
	private AID[] agtInventory = null;
	private AID[] agtExecutor = null;
	private AID[] agtCalculator = null;
	private long lastTime = 0;
	
	private FilterEvent filterEvent=null;

	protected void setup() {

		DFsrv.registerAgent("manager", this);

		// recupero agente inventory
		if (agtInventory==null) agtInventory = DFsrv.getTypeAgents("manager-inventory", this);

		// recupero agente executor
		if (agtExecutor==null) agtExecutor = DFsrv.getTypeAgents("executor", this);

		// recupero agente calculator
		if (agtCalculator==null) agtCalculator = DFsrv.getTypeAgents("calculator", this);

		log.info("Apro Connessione al db di inventario");
		db = new DbManager();
		conDb = db.open();
		dbData = new DbData(conDb);


		// Add the behaviour serving queries from controller agent
		addBehaviour(new Dispatcher(this,30*1000));
		
		// define filter
		filterEvent = new FilterEvent();
		addBehaviour(new ProcessEvent(this,10*1000));
	}


	// Put agent clean-up operations here
	protected void takeDown() {
		// Deregister from the yellow pages
		try {
			DFService.deregister(this);
			db.close();
		}
		catch (FIPAException fe) {
			fe.printStackTrace();
		}
	}


	private class Dispatcher extends TickerBehaviour  {

		public Dispatcher(Agent a, long period) {
			super(a, period);
		}

		@Override
		protected void onTick() {
			// azioni che eseguo ciclicamente

			// aggiorno la lista degli agenti calculator 
			if (agtCalculator==null) agtCalculator = DFsrv.getTypeAgents("calculator", myAgent);


			long step =5*60 * 1000;
			long offset = 5*60 * 1000; // tempo arretrato per gestire i casini
			long timestamp = System.currentTimeMillis();

			long stopTime = ((timestamp / step) * step)- offset;  // normalizzo timestamp
			long startTime= stopTime-step;	

			if (startTime!= lastTime){
				// e' scattato un nuovo intervallo eseguo il calcolo dei costi
				lastTime=startTime;

				log.info (" New start accounting interval :" +new Date(timestamp));

				ACLMessage req = new ACLMessage(ACLMessage.REQUEST);

				// TODO fare sw per agganciare orologio server che scatti a 5 o 15 minuti esatti

				req.addUserDefinedParameter("START_TIME",  new String().valueOf(startTime) );
				req.addUserDefinedParameter("STOP_TIME",  new String().valueOf(stopTime) );
				req.addUserDefinedParameter("STEP_TIME",  new String().valueOf(step) );


				if (agtCalculator.length >0) {
					// ci sono disponibili calculators
					req.addReceiver(agtCalculator[0]); // prendo il primo della lista

					try {
						ResultSet alldata = dbData.getAllResourceAndElement(Constants.RESOURCE_STATUS_ACTIVE);

						long lastIdResource=-1; 
						boolean first = true;
						boolean isAdd = false;
						Resource rsc=null;
						ArrayList<Element> elements = null;
						// ad ogni cambio di ID corrisponde un nuovo servizio
						while (alldata.next()) {

							long id = alldata.getLong("ID");

							if ( id!=lastIdResource) {
								if (!first) {
									// prima di creare il nuovo oggetto devo processare il vecchio
									try {
										req.setContentObject(rsc);
									} catch (IOException e) {
										e.printStackTrace();
									}
									myAgent.send(req);
									log.info("Risorsa mandata al calculator ha id = "+rsc.getId());
									first = false;
									isAdd = true;
								}  
								// ho trovato una risorsa nuova inizializzo l'oggetto
								lastIdResource = id;
								rsc = new Resource();
								rsc.setId(id);

								rsc.setCustomer(alldata.getString("CUSTOMER"));
								rsc.setService(alldata.getString("SERVICE"));
								rsc.setExternalCustomerId(alldata.getString("EXTERNAL_CUSTOMER_ID"));

								rsc.setResourceName(alldata.getString("RESOURCE_NAME"));
								rsc.setResourceType(alldata.getString("RESOURCE_TYPE"));
								rsc.setOffer(alldata.getString("RESOURCE_OFFER"));

								rsc.setExternalResourceId(alldata.getLong("EXTERNAL_RESOURCE_ID"));
								rsc.setStatus(alldata.getString("RESOURCE_STATUS"));
								elements =   new ArrayList<Element> ();
								rsc.setElements(elements);
								
								// DA CHIRIRE DOVE STA LA GESTIONE DEI COUNTER
								ArrayList<ResourceCounter> counters = dbData.getCounters(rsc.getId());
								if (counters != null) {
									log.info("Trovati counters su resource id:" + rsc.getId());
				
								}
								rsc.setCounters(counters);

								isAdd=false;
								first = false;

								// aggiungo gli elementi alla risorsa esistente

							}

							Element ele = new Element();

							ele.setId(alldata.getLong("ELEMENT_ID"));
							ele.setCustomerResourceId(alldata.getLong("CUSTOMER_RESOURCE_ID"));
							ele.setExternalId(alldata.getLong("ELEMENT_EXTERNAL_ID"));

							ele.setName(alldata.getString("ELEMENT_NAME"));
							ele.setType(alldata.getString("ELEMENT_TYPE"));
							ele.setValue(alldata.getString("ELEMENT_VALUE"));

							ele.setPerformanceDescriptionId(alldata.getLong("PERFORMANCE_DESCRIPTION_ID"));
							ele.setNeedPerformanceData(alldata.getBoolean("NEED_PERFORMANCE_DATA"));
							

							
							elements.add(ele);

						} // fine ciclo risorse ed element
						
						if (!isAdd && (rsc != null &&  elements != null)){

							try {
								req.setContentObject(rsc);
							} catch (IOException e) {
								// TODO Auto-generated catch block
								e.printStackTrace();
							}
							myAgent.send(req);
							isAdd=true;
							log.info("Mandata ULTIMA risorsa al calculator con  id = "+rsc.getId());
						}
						alldata.close();

					} catch (SQLException e) {
						// TODO Auto-generated catch block
						conDb = db.open();
						e.printStackTrace();
					}  // fine if controllo alldata not null
				}  

			} // end if chek lastTime
		}

	}
	private class ProcessEvent extends TickerBehaviour  {

		public ProcessEvent(Agent a, long period) {
			super(a, period);
		}

		@Override
		protected void onTick() {

			if (agtInventory==null) agtInventory = DFsrv.getTypeAgents("manager-inventory", myAgent);
			if (agtExecutor==null) agtExecutor = DFsrv.getTypeAgents("executor", myAgent);

			if (agtInventory!=null && agtExecutor!=null ){

				LoadEvents ld = new LoadEvents();
				Accountit events = ld.getEvents();
				if (events != null){
					// gestisco l'inn serimento degli eventi recuperati.
					for (Event evt : events.getEvents()){
						
						// check  filter on event
						if (!filterEvent.discards(evt)){
							
							// mando messaggio
							ACLMessage sendTo = new ACLMessage(ACLMessage.REQUEST);
							sendTo.addUserDefinedParameter("TYPE",  "EVENT" );
							sendTo.addReceiver(agtInventory[0]);
							sendTo.addReceiver(agtExecutor[0]);

							try {
								
								sendTo.setContentObject(evt);
							} catch (IOException e) {
								// TODO Auto-generated catch block
								e.printStackTrace();
								
							}
							log.info("Inviato al Agent inventory evento con descrizione = " + evt.getDescription());
							myAgent.send(sendTo);
						}
					} // end for
				}// end verifica nuovi file eventi

			} else {
				log.info("manca o agente inventory o un executor");
			}
		} // end on tick

	} // end class
}
